# -----------------------------------------------------------------------------
# Copyright (c) 2009-2016 Nicolas P. Rougier. All rights reserved.
# Distributed under the (new) BSD License.
# -----------------------------------------------------------------------------
""" Fast and failsafe GL console """
import numpy as np
from glumpy import gl, glm, gloo


# Translated from
# http://www.piclist.com/tecHREF/datafile/charset/extractor/charset_extractor.htm
__font_6x8__ = np.array([
    (0x00,0x00,0x00,0x00,0x00,0x00), (0x10,0xE3,0x84,0x10,0x01,0x00),
    (0x6D,0xB4,0x80,0x00,0x00,0x00), (0x00,0xA7,0xCA,0x29,0xF2,0x80),
    (0x20,0xE4,0x0C,0x09,0xC1,0x00), (0x65,0x90,0x84,0x21,0x34,0xC0),
    (0x21,0x45,0x08,0x55,0x23,0x40), (0x30,0xC2,0x00,0x00,0x00,0x00),
    (0x10,0x82,0x08,0x20,0x81,0x00), (0x20,0x41,0x04,0x10,0x42,0x00),
    (0x00,0xA3,0x9F,0x38,0xA0,0x00), (0x00,0x41,0x1F,0x10,0x40,0x00),
    (0x00,0x00,0x00,0x00,0xC3,0x08), (0x00,0x00,0x1F,0x00,0x00,0x00),
    (0x00,0x00,0x00,0x00,0xC3,0x00), (0x00,0x10,0x84,0x21,0x00,0x00),
    (0x39,0x14,0xD5,0x65,0x13,0x80), (0x10,0xC1,0x04,0x10,0x43,0x80),
    (0x39,0x10,0x46,0x21,0x07,0xC0), (0x39,0x10,0x4E,0x05,0x13,0x80),
    (0x08,0x62,0x92,0x7C,0x20,0x80), (0x7D,0x04,0x1E,0x05,0x13,0x80),
    (0x18,0x84,0x1E,0x45,0x13,0x80), (0x7C,0x10,0x84,0x20,0x82,0x00),
    (0x39,0x14,0x4E,0x45,0x13,0x80), (0x39,0x14,0x4F,0x04,0x23,0x00),
    (0x00,0x03,0x0C,0x00,0xC3,0x00), (0x00,0x03,0x0C,0x00,0xC3,0x08),
    (0x08,0x42,0x10,0x20,0x40,0x80), (0x00,0x07,0xC0,0x01,0xF0,0x00),
    (0x20,0x40,0x81,0x08,0x42,0x00), (0x39,0x10,0x46,0x10,0x01,0x00),
    (0x39,0x15,0xD5,0x5D,0x03,0x80), (0x39,0x14,0x51,0x7D,0x14,0x40),
    (0x79,0x14,0x5E,0x45,0x17,0x80), (0x39,0x14,0x10,0x41,0x13,0x80),
    (0x79,0x14,0x51,0x45,0x17,0x80), (0x7D,0x04,0x1E,0x41,0x07,0xC0),
    (0x7D,0x04,0x1E,0x41,0x04,0x00), (0x39,0x14,0x17,0x45,0x13,0xC0),
    (0x45,0x14,0x5F,0x45,0x14,0x40), (0x38,0x41,0x04,0x10,0x43,0x80),
    (0x04,0x10,0x41,0x45,0x13,0x80), (0x45,0x25,0x18,0x51,0x24,0x40),
    (0x41,0x04,0x10,0x41,0x07,0xC0), (0x45,0xB5,0x51,0x45,0x14,0x40),
    (0x45,0x95,0x53,0x45,0x14,0x40), (0x39,0x14,0x51,0x45,0x13,0x80),
    (0x79,0x14,0x5E,0x41,0x04,0x00), (0x39,0x14,0x51,0x55,0x23,0x40),
    (0x79,0x14,0x5E,0x49,0x14,0x40), (0x39,0x14,0x0E,0x05,0x13,0x80),
    (0x7C,0x41,0x04,0x10,0x41,0x00), (0x45,0x14,0x51,0x45,0x13,0x80),
    (0x45,0x14,0x51,0x44,0xA1,0x00), (0x45,0x15,0x55,0x55,0x52,0x80),
    (0x45,0x12,0x84,0x29,0x14,0x40), (0x45,0x14,0x4A,0x10,0x41,0x00),
    (0x78,0x21,0x08,0x41,0x07,0x80), (0x38,0x82,0x08,0x20,0x83,0x80),
    (0x01,0x02,0x04,0x08,0x10,0x00), (0x38,0x20,0x82,0x08,0x23,0x80),
    (0x10,0xA4,0x40,0x00,0x00,0x00), (0x00,0x00,0x00,0x00,0x00,0x3F),
    (0x30,0xC1,0x00,0x00,0x00,0x00), (0x00,0x03,0x81,0x3D,0x13,0xC0),
    (0x41,0x07,0x91,0x45,0x17,0x80), (0x00,0x03,0x91,0x41,0x13,0x80),
    (0x04,0x13,0xD1,0x45,0x13,0xC0), (0x00,0x03,0x91,0x79,0x03,0x80),
    (0x18,0x82,0x1E,0x20,0x82,0x00), (0x00,0x03,0xD1,0x44,0xF0,0x4E),
    (0x41,0x07,0x12,0x49,0x24,0x80), (0x10,0x01,0x04,0x10,0x41,0x80),
    (0x08,0x01,0x82,0x08,0x24,0x8C), (0x41,0x04,0x94,0x61,0x44,0x80),
    (0x10,0x41,0x04,0x10,0x41,0x80), (0x00,0x06,0x95,0x55,0x14,0x40),
    (0x00,0x07,0x12,0x49,0x24,0x80), (0x00,0x03,0x91,0x45,0x13,0x80),
    (0x00,0x07,0x91,0x45,0x17,0x90), (0x00,0x03,0xD1,0x45,0x13,0xC1),
    (0x00,0x05,0x89,0x20,0x87,0x00), (0x00,0x03,0x90,0x38,0x13,0x80),
    (0x00,0x87,0x88,0x20,0xA1,0x00), (0x00,0x04,0x92,0x49,0x62,0x80),
    (0x00,0x04,0x51,0x44,0xA1,0x00), (0x00,0x04,0x51,0x55,0xF2,0x80),
    (0x00,0x04,0x92,0x31,0x24,0x80), (0x00,0x04,0x92,0x48,0xE1,0x18),
    (0x00,0x07,0x82,0x31,0x07,0x80), (0x18,0x82,0x18,0x20,0x81,0x80),
    (0x10,0x41,0x00,0x10,0x41,0x00), (0x30,0x20,0x83,0x08,0x23,0x00),
    (0x29,0x40,0x00,0x00,0x00,0x00), (0x10,0xE6,0xD1,0x45,0xF0,0x00)], dtype=np.float32)

__vertex__ = """
#version 120
uniform mat4 projection;
uniform float scale;
uniform vec4 color;
attribute vec2 position;
attribute vec3 bytes_012, bytes_345;
varying vec4 v_color;
varying vec3 v_bytes_012, v_bytes_345;
void main (void)
{
    gl_Position = projection*vec4(position*scale, 0.0, 1.0);
    gl_PointSize = 8.0 * scale;

    v_color = color;
    v_bytes_012 = bytes_012;
    v_bytes_345 = bytes_345;
}
"""

__fragment__ = """
#version 120
float segment(float edge0, float edge1, float x)
{
    return step(edge0,x) * (1.0-step(edge1,x));
}
varying vec4 v_color;
varying vec3 v_bytes_012, v_bytes_345;
void main(void)
{
    vec2 uv = floor(gl_PointCoord.xy * 8.0);
    if(uv.x > 5.0) discard;
    if(uv.y > 7.0) discard;
    float index  = floor( (uv.y*6.0+uv.x)/8.0 );
    float offset = floor( mod(uv.y*6.0+uv.x,8.0));
    float byte = segment(0.0,1.0,index) * v_bytes_012.x
               + segment(1.0,2.0,index) * v_bytes_012.y
               + segment(2.0,3.0,index) * v_bytes_012.z
               + segment(3.0,4.0,index) * v_bytes_345.x
               + segment(4.0,5.0,index) * v_bytes_345.y
               + segment(5.0,6.0,index) * v_bytes_345.z;
    if( floor(mod(byte / (128.0/pow(2.0,offset)), 2.0)) > 0.0 )
        gl_FragColor = v_color;
    else
        discard;
}
"""


class Console(object):
    """ Fast and failsafe GL console """

    def __init__(self, rows, cols, scale=2, color=(0,0,0,1)):

        # Harcoded because of font above and shader program
        self._cwidth = 6
        self._cheight = 8
        self._scale = int(max(scale,1))

        dtype = [("position", np.float32, 2),
                 ("glyph",    np.float32, 6)]
        self._program = gloo.Program(__vertex__, __fragment__)
        self._data = np.zeros((rows,cols), dtype).view(gloo.VertexBuffer)
        dtype = [("position",  np.float32, 2),
                 ("bytes_012", np.float32, 3),
                 ("bytes_345", np.float32, 3)]
        self._program.bind(self._data.view(dtype))

        # Initialize glyph position (they won't move)
        C,R = np.meshgrid(np.arange(cols), np.arange(rows))
        self._data['position'][...,0] = 4.0 + self.cwidth*C
        self._data['position'][...,1] = 4.0 + self.cheight*R

        self._program['scale'] = self._scale
        self._program['color'] = color
        self._rows, self._cols = rows, cols
        self._row = 0


    @property
    def scale(self):
        return self._scale


    @property
    def rows(self):
        return self._rows


    @property
    def cols(self):
        return self._cols


    @property
    def cwidth(self):
        return self._cwidth


    @property
    def cheight(self):
        return self._cheight + 2


    def on_resize(self, width, height):
        """ Update console projection """

        self._program["projection"] = glm.ortho(0, width, height, 0, -1, +1)


    def draw(self):
        """ Draw console """

        self._program.draw(gl.GL_POINTS)


    def clear(self):
        """ Clear console """

        self._data["glyph"] = 0
        self._row = 0


    def write(self, text=""):
        """ Write text and scroll """

        # Clear line
        self._data["glyph"][self._row] = 0

        if len(text):
            # Crop text if necessary
            text = text[:self._cols]
            # Write text
            I = np.array([ord(c)-32 for c in text])
            self._data["glyph"][self._row,:len(text)] = __font_6x8__[I]

        # Update row and scroll if necessary
        self._row += 1
        if self._row > self._rows-1:
            self._data["glyph"][:-n] = self._data["glyph"][n:]
            self._data["glyph"][-n:] = 0
            self._row = self._rows-1
